﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using DomainDriver.DomainModeling.DomainObjects;
using DomainDriver.DomainModeling.Queries;
using DomainDriver.DomainModeling.StorageManagers.DataSetStorage;
using DomainDriver.DomainModeling.StorageManagers.EntityFrameworkStorage;
using ObjectRow = AutomotivePricingModel.AutomotivePricingDataSet.DealersRow;
using NestedBrandRow = AutomotivePricingModel.AutomotivePricingDataSet.DealerBrandRelationsRow;
using NestedVehicleRow = AutomotivePricingModel.AutomotivePricingDataSet.DealerVehicleRelationsRow;

namespace AutomotivePricingModel.Dealers
{
    public partial class Dealer : IEfEntity<Guid>, IEfAggregateRoot<Dealer>, IDataSetSerializable<AutomotivePricingDataSet, DataRow>
    {
        #region ADO .NET Entities Framework Storage

        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public Guid EF_Key
        {
            get { return m_Key; }
            set { m_Key = value; }
        }

        [NotMapped]
        public ICollection<DealerBrandRelation> EF_BrandsRepresented
        {
            get { return m_BrandsRepresented.Values; }
            set
            {
                m_BrandsRepresented.Clear();
                foreach (DealerBrandRelation brandRelation in value)
                {
                    m_BrandsRepresented.Add(brandRelation.RelatedBrandId, brandRelation);
                }
            }
        }

        [NotMapped]
        public ICollection<DealerVehicleRelation> EF_VehiclesInStock
        {
            get { return m_VehiclesInStock.Values; }
            set
            {
                m_VehiclesInStock.Clear();
                foreach (DealerVehicleRelation vehicleRelation in value)
                {
                    m_VehiclesInStock.Add(vehicleRelation.RelatedVehicleId, vehicleRelation);
                }
            }
        }

        ICollection<QuerySpecification> IEfAggregateRoot<Dealer>.OverriddenQueries
        {
            get
            {
                List<QuerySpecification> overriddenQueries = new List<QuerySpecification>();
                overriddenQueries.Add(new QuerySpecification(typeof(ContainsQuery<Dealer>), new string[] { "BrandsRepresented" }));
                overriddenQueries.Add(new QuerySpecification(typeof(ContainsQuery<Dealer>), new string[] { "VehiclesInStock" }));
                return overriddenQueries;
            }
        }

        bool IEfAggregateRoot<Dealer>.SearchNestedAggregateValues(DbContext context, IParameterizedQuery<Dealer> query)
        {
            IDictionary<string, object> parameterValues = query.ParameterValues;
            int brandParameterCount = 1;
            int vehicleParameterCount = 1;
            string brandParameterName = "BrandsRepresented";
            string vehicleParameterName = "VehiclesInStock";

            if ((query is ContainsQuery<Dealer>) && (parameterValues.Count == brandParameterCount) && (parameterValues.ContainsKey(brandParameterName)))
            {
                Guid brandId = (Guid)parameterValues[brandParameterName];

                Expression<Func<DealerBrandRelation, bool>> childQueryExpression = ((DealerBrandRelation rel) => ((rel.ParentDealerId == this.Key) && (rel.RelatedBrandId == brandId)));
                IEnumerable<DealerBrandRelation> childQueryResults = context.Set<DealerBrandRelation>().Where<DealerBrandRelation>(childQueryExpression.Compile());
                bool containsChild = (childQueryResults.Count<DealerBrandRelation>() > 0);
                return containsChild;
            }

            if ((query is ContainsQuery<Dealer>) && (parameterValues.Count == vehicleParameterCount) && (parameterValues.ContainsKey(vehicleParameterName)))
            {
                Guid vehicleId = (Guid)parameterValues[vehicleParameterName];

                Expression<Func<DealerVehicleRelation, bool>> childQueryExpression = ((DealerVehicleRelation rel) => ((rel.ParentDealerId == this.Key) && (rel.RelatedVehicleId == vehicleId)));
                IEnumerable<DealerVehicleRelation> childQueryResults = context.Set<DealerVehicleRelation>().Where<DealerVehicleRelation>(childQueryExpression.Compile());
                bool containsChild = (childQueryResults.Count<DealerVehicleRelation>() > 0);
                return containsChild;
            }

            throw new NotImplementedException();
        }

        void IEfAggregateRoot<Dealer>.ReadNestedAggregateValues(DbContext context)
        {
            IQueryable<DealerBrandRelation> brandRelations = context.Set<DealerBrandRelation>().Where<DealerBrandRelation>((DealerBrandRelation rel) => rel.ParentDealerId == this.Key);
            List<DealerBrandRelation> brandRelationList = new List<DealerBrandRelation>(brandRelations);
            this.EF_BrandsRepresented = brandRelationList;

            IQueryable<DealerVehicleRelation> vehicleRelations = context.Set<DealerVehicleRelation>().Where<DealerVehicleRelation>((DealerVehicleRelation rel) => rel.ParentDealerId == this.Key);
            List<DealerVehicleRelation> vehicleRelationList = new List<DealerVehicleRelation>(vehicleRelations);
            this.EF_VehiclesInStock = vehicleRelationList;
        }

        void IEfAggregateRoot<Dealer>.AddNestedAggregateValues(DbContext context)
        {
            Expression<Func<ICollection<DealerBrandRelation>>> nestedBrandsGetterExpression = () => this.EF_BrandsRepresented;
            EfAggregateUtilities.AddNestedValues<DealerBrandRelation>(context, nestedBrandsGetterExpression);

            Expression<Func<ICollection<DealerVehicleRelation>>> nestedVehiclesGetterExpression = () => this.EF_VehiclesInStock;
            EfAggregateUtilities.AddNestedValues<DealerVehicleRelation>(context, nestedVehiclesGetterExpression);
        }

        void IEfAggregateRoot<Dealer>.UpdateNestedAggregateValues(DbContext context, Dealer originalAggregateRoot)
        {
            Expression<Func<DealerBrandRelation, Guid>> brandKeyGetterExpression = (DealerBrandRelation nestedObj) => nestedObj.RelatedBrandId;
            Expression<Func<DealerBrandRelation, DealerBrandRelation, bool>> brandsMatchExpression = (DealerBrandRelation obj1, DealerBrandRelation obj2) => ((obj1.ParentDealerId == obj2.ParentDealerId) && (obj1.RelatedBrandId == obj2.RelatedBrandId));
            Expression<Func<Dealer, IDictionary<Guid, DealerBrandRelation>>> brandDictionaryGetterExpression = (Dealer agg) => agg.m_BrandsRepresented;
            EfAggregateUtilities.UpdateNestedValues<Dealer, Guid, DealerBrandRelation>(context, this, originalAggregateRoot, brandKeyGetterExpression, brandsMatchExpression, brandDictionaryGetterExpression);

            Expression<Func<DealerVehicleRelation, Guid>> vehicleKeyGetterExpression = (DealerVehicleRelation nestedObj) => nestedObj.RelatedVehicleId;
            Expression<Func<DealerVehicleRelation, DealerVehicleRelation, bool>> vehiclesMatchExpression = (DealerVehicleRelation obj1, DealerVehicleRelation obj2) => ((obj1.ParentDealerId == obj2.ParentDealerId) && (obj1.RelatedVehicleId == obj2.RelatedVehicleId));
            Expression<Func<Dealer, IDictionary<Guid, DealerVehicleRelation>>> vehicleDictionaryGetterExpression = (Dealer agg) => agg.m_VehiclesInStock;
            EfAggregateUtilities.UpdateNestedValues<Dealer, Guid, DealerVehicleRelation>(context, this, originalAggregateRoot, vehicleKeyGetterExpression, vehiclesMatchExpression, vehicleDictionaryGetterExpression);
        }

        void IEfAggregateRoot<Dealer>.DeleteNestedAggregateValues(DbContext context)
        {
            Expression<Func<DealerBrandRelation, bool>> brandDeleteQueryExpression = ((DealerBrandRelation rel) => (rel.ParentDealerId == this.Key));
            EfAggregateUtilities.DeleteNestedValues(context, brandDeleteQueryExpression);

            Expression<Func<DealerVehicleRelation, bool>> vehicleDeleteQueryExpression = ((DealerVehicleRelation rel) => (rel.ParentDealerId == this.Key));
            EfAggregateUtilities.DeleteNestedValues(context, vehicleDeleteQueryExpression);
        }

        #endregion

        #region AutomotivePricingDataSet Serialization

        string IDataSetSerializable<AutomotivePricingDataSet, DataRow>.ObjectTableName
        {
            get { return AutomotivePricingDataSet.SchemaHelper.Dealers.TableName; }
        }

        string IDataSetSerializable<AutomotivePricingDataSet, DataRow>.KeyColumnName
        {
            get { return AutomotivePricingDataSet.SchemaHelper.Dealers.EF_DealerIdColumn.ColumnName; }
        }

        void IDataSetSerializable<AutomotivePricingDataSet, DataRow>.Serialize(AutomotivePricingDataSet datasource, DataRow dataRow)
        {
            ObjectRow objectRecord = dataRow as ObjectRow;

            objectRecord.EF_DealerId = m_Key;
            objectRecord.Name = m_Name;

            foreach (DealerBrandRelation brandRelation in m_BrandsRepresented.Values)
            {
                NestedBrandRow brandRow = datasource.DealerBrandRelations.NewDealerBrandRelationsRow();
                brandRow.ParentDealerId = brandRelation.ParentDealerId;
                brandRow.RelatedBrandId = brandRelation.RelatedBrandId;
                datasource.DealerBrandRelations.AddDealerBrandRelationsRow(brandRow);
            }

            foreach (DealerVehicleRelation vehicleRelation in m_VehiclesInStock.Values)
            {
                NestedVehicleRow vehicleRow = datasource.DealerVehicleRelations.NewDealerVehicleRelationsRow();
                vehicleRow.ParentDealerId = vehicleRelation.ParentDealerId;
                vehicleRow.RelatedVehicleId = vehicleRelation.RelatedVehicleId;
                vehicleRow.VehicleCount = vehicleRelation.VehicleCount;
                datasource.DealerVehicleRelations.AddDealerVehicleRelationsRow(vehicleRow);
            }
        }

        void IDataSetSerializable<AutomotivePricingDataSet, DataRow>.Deserialize(AutomotivePricingDataSet datasource, DataRow dataRow)
        {
            ObjectRow objectRecord = dataRow as ObjectRow;

            m_Key = objectRecord.EF_DealerId;
            m_Name = objectRecord.Name;

            m_BrandsRepresented.Clear();
            foreach (NestedBrandRow brandRow in objectRecord.GetDealerBrandRelationsRows())
            {
                DealerBrandRelation brandRelation = new DealerBrandRelation();
                brandRelation.ParentDealerId = brandRow.ParentDealerId;
                brandRelation.RelatedBrandId = brandRow.RelatedBrandId;
                m_BrandsRepresented.Add(brandRelation.RelatedBrandId, brandRelation);
            }

            m_VehiclesInStock.Clear();
            foreach (NestedVehicleRow vehicleRow in objectRecord.GetDealerVehicleRelationsRows())
            {
                DealerVehicleRelation vehicleRelation = new DealerVehicleRelation();
                vehicleRelation.ParentDealerId = vehicleRow.ParentDealerId;
                vehicleRelation.RelatedVehicleId = vehicleRow.RelatedVehicleId;
                vehicleRelation.VehicleCount = vehicleRow.VehicleCount;
                m_VehiclesInStock.Add(vehicleRelation.RelatedVehicleId, vehicleRelation);
            }
        }

        #endregion
    }
}